{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![](https://i.imgur.com/eBRPvWB.png)\n",
    "\n",
    "# Practical PyTorch: Translation with a Sequence to Sequence Network and Attention\n",
    "\n",
    "In this project we will be teaching a neural network to translate from French to English.\n",
    "\n",
    "```\n",
    "[KEY: > input, = target, < output]\n",
    "\n",
    "> il est en train de peindre un tableau .\n",
    "= he is painting a picture .\n",
    "< he is painting a picture .\n",
    "\n",
    "> pourquoi ne pas essayer ce vin delicieux ?\n",
    "= why not try that delicious wine ?\n",
    "< why not try that delicious wine ?\n",
    "\n",
    "> elle n est pas poete mais romanciere .\n",
    "= she is not a poet but a novelist .\n",
    "< she not not a poet but a novelist .\n",
    "\n",
    "> vous etes trop maigre .\n",
    "= you re too skinny .\n",
    "< you re all alone .\n",
    "```\n",
    "\n",
    "... to varying degrees of success.\n",
    "\n",
    "This is made possible by the simple but powerful idea of the [sequence to sequence network](http://arxiv.org/abs/1409.3215), in which two recurrent neural networks work together to transform one sequence to another. An encoder network condenses an input sequence into a single vector, and a decoder network unfolds that vector into a new sequence.\n",
    "\n",
    "To improve upon this model we'll use an [attention mechanism](https://arxiv.org/abs/1409.0473), which lets the decoder learn to focus over a specific range of the input sequence."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# The Sequence to Sequence model\n",
    "\n",
    "A [Sequence to Sequence network](http://arxiv.org/abs/1409.3215), or seq2seq network, or [Encoder Decoder network](https://arxiv.org/pdf/1406.1078v3.pdf), is a model consisting of two separate RNNs called the **encoder** and **decoder**. The encoder reads an input sequence one item at a time, and outputs a vector at each step. The final output of the encoder is kept as the **context** vector. The decoder uses this context vector to produce a sequence of outputs one step at a time.\n",
    "\n",
    "![](https://i.imgur.com/tVtHhNp.png)\n",
    "\n",
    "When using a single RNN, there is a one-to-one relationship between inputs and outputs. We would quickly run into problems with different sequence orders and lengths that are common during translation. Consider the simple sentence \"Je ne suis pas le chat noir\" &rarr; \"I am not the black cat\". Many of the words have a pretty direct translation, like \"chat\" &rarr; \"cat\". However the differing grammars cause words to be in different orders, e.g. \"chat noir\" and \"black cat\". There is also the \"ne ... pas\" &rarr; \"not\" construction that makes the two sentences have different lengths.\n",
    "\n",
    "With the seq2seq model, by encoding many inputs into one vector, and decoding from one vector into many outputs, we are freed from the constraints of sequence order and length. The encoded sequence is represented by a single vector, a single point in some N dimensional space of sequences. In an ideal case, this point can be considered the \"meaning\" of the sequence.\n",
    "\n",
    "This idea can be extended beyond sequences. Image captioning tasks take an [image as input, and output a description](https://arxiv.org/abs/1411.4555) of the image (img2seq). Some image generation tasks take a [description as input and output a generated image](https://arxiv.org/abs/1511.02793) (seq2img). These models can be referred to more generally as \"encoder decoder\" networks."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The Attention Mechanism\n",
    "\n",
    "The fixed-length vector carries the burden of encoding the the entire \"meaning\" of the input sequence, no matter how long that may be. With all the variance in language, this is a very hard problem. Imagine two nearly identical sentences, twenty words long, with only one word different. Both the encoders and decoders must be nuanced enough to represent that change as a very slightly different point in space.\n",
    "\n",
    "The **attention mechanism** [introduced by Bahdanau et al.](https://arxiv.org/abs/1409.0473) addresses this by giving the decoder a way to \"pay attention\" to parts of the input, rather than relying on a single vector. For every step the decoder can select a different part of the input sentence to consider.\n",
    "\n",
    "![](https://i.imgur.com/5y6SCvU.png)\n",
    "\n",
    "Attention is calculated with another feedforward layer in the decoder. This layer will use the current input and hidden state to create a new vector, which is the same size as the input sequence (in practice, a fixed maximum length). This vector is processed through softmax to create *attention weights*, which are multiplied by the encoders' outputs to create a new context vector, which is then used to predict the next output.\n",
    "\n",
    "![](https://i.imgur.com/K1qMPxs.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Requirements\n",
    "\n",
    "You will need [PyTorch](http://pytorch.org/) to build and train the models, and [matplotlib](https://matplotlib.org/) for plotting training and visualizing attention outputs later."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import unicodedata\n",
    "import string\n",
    "import re\n",
    "import random\n",
    "import time\n",
    "import math\n",
    "\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "from torch.autograd import Variable\n",
    "from torch import optim\n",
    "import torch.nn.functional as F"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we will also define a constant to decide whether to use the GPU (with CUDA specifically) or the CPU. **If you don't have a GPU, set this to `False`**. Later when we create tensors, this variable will be used to decide whether we keep them on CPU or move them to GPU."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "USE_CUDA = True"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Loading data files\n",
    "\n",
    "The data for this project is a set of many thousands of English to French translation pairs.\n",
    "\n",
    "[This question on Open Data Stack Exchange](http://opendata.stackexchange.com/questions/3888/dataset-of-sentences-translated-into-many-languages) pointed me to the open translation site http://tatoeba.org/ which has downloads available at http://tatoeba.org/eng/downloads - and better yet, someone did the extra work of splitting language pairs into individual text files here: http://www.manythings.org/anki/\n",
    "\n",
    "The English to French pairs are too big to include in the repo, so download `fra-eng.zip`, extract the text file in there, and rename it to `data/eng-fra.txt` before continuing (for some reason the zipfile is named backwards). The file is a tab separated list of translation pairs:\n",
    "\n",
    "```\n",
    "I am cold.    J'ai froid.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Similar to the character encoding used in the character-level RNN tutorials, we will be representing each word in a language as a one-hot vector, or giant vector of zeros except for a single one (at the index of the word). Compared to the dozens of characters that might exist in a language, there are many many more words, so the encoding vector is much larger. We will however cheat a bit and trim the data to only use a few thousand words per language."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Indexing words\n",
    "\n",
    "We'll need a unique index per word to use as the inputs and targets of the networks later. To keep track of all this we will use a helper class called `Lang` which has word &rarr; index (`word2index`) and index &rarr; word (`index2word`) dictionaries, as well as a count of each word `word2count` to use to later replace rare words."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "SOS_token = 0\n",
    "EOS_token = 1\n",
    "\n",
    "class Lang:\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        self.word2index = {}\n",
    "        self.word2count = {}\n",
    "        self.index2word = {0: \"SOS\", 1: \"EOS\"}\n",
    "        self.n_words = 2 # Count SOS and EOS\n",
    "      \n",
    "    def index_words(self, sentence):\n",
    "        for word in sentence.split(' '):\n",
    "            self.index_word(word)\n",
    "\n",
    "    def index_word(self, word):\n",
    "        if word not in self.word2index:\n",
    "            self.word2index[word] = self.n_words\n",
    "            self.word2count[word] = 1\n",
    "            self.index2word[self.n_words] = word\n",
    "            self.n_words += 1\n",
    "        else:\n",
    "            self.word2count[word] += 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Reading and decoding files\n",
    "\n",
    "The files are all in Unicode, to simplify we will turn Unicode characters to ASCII, make everything lowercase, and trim most punctuation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Turn a Unicode string to plain ASCII, thanks to http://stackoverflow.com/a/518232/2809427\n",
    "def unicode_to_ascii(s):\n",
    "    return ''.join(\n",
    "        c for c in unicodedata.normalize('NFD', s)\n",
    "        if unicodedata.category(c) != 'Mn'\n",
    "    )\n",
    "\n",
    "# Lowercase, trim, and remove non-letter characters\n",
    "def normalize_string(s):\n",
    "    s = unicode_to_ascii(s.lower().strip())\n",
    "    s = re.sub(r\"([.!?])\", r\" \\1\", s)\n",
    "    s = re.sub(r\"[^a-zA-Z.!?]+\", r\" \", s)\n",
    "    return s"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To read the data file we will split the file into lines, and then split lines into pairs. The files are all English &rarr; Other Language, so if we want to translate from Other Language &rarr; English I added the `reverse` flag to reverse the pairs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def read_langs(lang1, lang2, reverse=False):\n",
    "    print(\"Reading lines...\")\n",
    "\n",
    "    # Read the file and split into lines\n",
    "    lines = open('../data/%s-%s.txt' % (lang1, lang2)).read().strip().split('\\n')\n",
    "    \n",
    "    # Split every line into pairs and normalize\n",
    "    pairs = [[normalize_string(s) for s in l.split('\\t')] for l in lines]\n",
    "    \n",
    "    # Reverse pairs, make Lang instances\n",
    "    if reverse:\n",
    "        pairs = [list(reversed(p)) for p in pairs]\n",
    "        input_lang = Lang(lang2)\n",
    "        output_lang = Lang(lang1)\n",
    "    else:\n",
    "        input_lang = Lang(lang1)\n",
    "        output_lang = Lang(lang2)\n",
    "        \n",
    "    return input_lang, output_lang, pairs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Filtering sentences\n",
    "\n",
    "Since there are a *lot* of example sentences and we want to train something quickly, we'll trim the data set to only relatively short and simple sentences. Here the maximum length is 10 words (that includes punctuation) and we're filtering to sentences that translate to the form \"I am\" or \"He is\" etc. (accounting for apostrophes being removed)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "MAX_LENGTH = 10\n",
    "\n",
    "good_prefixes = (\n",
    "    \"i am \", \"i m \",\n",
    "    \"he is\", \"he s \",\n",
    "    \"she is\", \"she s\",\n",
    "    \"you are\", \"you re \"\n",
    ")\n",
    "\n",
    "def filter_pair(p):\n",
    "    return len(p[0].split(' ')) < MAX_LENGTH and len(p[1].split(' ')) < MAX_LENGTH and \\\n",
    "        p[1].startswith(good_prefixes)\n",
    "\n",
    "def filter_pairs(pairs):\n",
    "    return [pair for pair in pairs if filter_pair(pair)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The full process for preparing the data is:\n",
    "\n",
    "* Read text file and split into lines, split lines into pairs\n",
    "* Normalize text, filter by length and content\n",
    "* Make word lists from sentences in pairs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Reading lines...\n",
      "Read 135842 sentence pairs\n",
      "Trimmed to 9129 sentence pairs\n",
      "Indexing words...\n",
      "['il est paresseux .', 'he s lazy .']\n"
     ]
    }
   ],
   "source": [
    "def prepare_data(lang1_name, lang2_name, reverse=False):\n",
    "    input_lang, output_lang, pairs = read_langs(lang1_name, lang2_name, reverse)\n",
    "    print(\"Read %s sentence pairs\" % len(pairs))\n",
    "    \n",
    "    pairs = filter_pairs(pairs)\n",
    "    print(\"Trimmed to %s sentence pairs\" % len(pairs))\n",
    "    \n",
    "    print(\"Indexing words...\")\n",
    "    for pair in pairs:\n",
    "        input_lang.index_words(pair[0])\n",
    "        output_lang.index_words(pair[1])\n",
    "\n",
    "    return input_lang, output_lang, pairs\n",
    "\n",
    "input_lang, output_lang, pairs = prepare_data('eng', 'fra', True)\n",
    "\n",
    "# Print an example pair\n",
    "print(random.choice(pairs))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Turning training data into Tensors/Variables\n",
    "\n",
    "To train we need to turn the sentences into something the neural network can understand, which of course means numbers. Each sentence will be split into words and turned into a Tensor, where each word is replaced with the index (from the Lang indexes made earlier). While creating these tensors we will also append the EOS token to signal that the sentence is over.\n",
    "\n",
    "![](https://i.imgur.com/LzocpGH.png)\n",
    "\n",
    "A Tensor is a multi-dimensional array of numbers, defined with some type e.g. FloatTensor or LongTensor. In this case we'll be using LongTensor to represent an array of integer indexes.\n",
    "\n",
    "Trainable PyTorch modules take Variables as input, rather than plain Tensors. A Variable is basically a Tensor that is able to keep track of the graph state, which is what makes autograd (automatic calculation of backwards gradients) possible."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Return a list of indexes, one for each word in the sentence\n",
    "def indexes_from_sentence(lang, sentence):\n",
    "    return [lang.word2index[word] for word in sentence.split(' ')]\n",
    "\n",
    "def variable_from_sentence(lang, sentence):\n",
    "    indexes = indexes_from_sentence(lang, sentence)\n",
    "    indexes.append(EOS_token)\n",
    "    var = Variable(torch.LongTensor(indexes).view(-1, 1))\n",
    "#     print('var =', var)\n",
    "    if USE_CUDA: var = var.cuda()\n",
    "    return var\n",
    "\n",
    "def variables_from_pair(pair):\n",
    "    input_variable = variable_from_sentence(input_lang, pair[0])\n",
    "    target_variable = variable_from_sentence(output_lang, pair[1])\n",
    "    return (input_variable, target_variable)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Building the models"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The Encoder\n",
    "\n",
    "<img src=\"images/encoder-network.png\" style=\"float: right\" />\n",
    "\n",
    "The encoder of a seq2seq network is a RNN that outputs some value for every word from the input sentence. For every input word the encoder outputs a vector and a hidden state, and uses the hidden state for the next input word."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class EncoderRNN(nn.Module):\n",
    "    def __init__(self, input_size, hidden_size, n_layers=1):\n",
    "        super(EncoderRNN, self).__init__()\n",
    "        \n",
    "        self.input_size = input_size\n",
    "        self.hidden_size = hidden_size\n",
    "        self.n_layers = n_layers\n",
    "        \n",
    "        self.embedding = nn.Embedding(input_size, hidden_size)\n",
    "        self.gru = nn.GRU(hidden_size, hidden_size, n_layers)\n",
    "        \n",
    "    def forward(self, word_inputs, hidden):\n",
    "        # Note: we run this all at once (over the whole input sequence)\n",
    "        seq_len = len(word_inputs)\n",
    "        embedded = self.embedding(word_inputs).view(seq_len, 1, -1)\n",
    "        output, hidden = self.gru(embedded, hidden)\n",
    "        return output, hidden\n",
    "\n",
    "    def init_hidden(self):\n",
    "        hidden = Variable(torch.zeros(self.n_layers, 1, self.hidden_size))\n",
    "        if USE_CUDA: hidden = hidden.cuda()\n",
    "        return hidden"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Attention Decoder"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interpreting the Bahdanau et al. model\n",
    "\n",
    "The attention model in [Neural Machine Translation by Jointly Learning to Align and Translate](https://arxiv.org/abs/1409.0473) is described as the following series of equations.\n",
    "\n",
    "Each decoder output is conditioned on the previous outputs and some $\\mathbf x$, where $\\mathbf x$ consists of the current hidden state (which takes into account previous outputs) and the attention \"context\", which is calculated below. The function $g$ is a fully-connected layer with a nonlinear activation, which takes as input the values $y_{i-1}$, $s_i$, and $c_i$ concatenated.\n",
    "\n",
    "$$\n",
    "p(y_i \\mid \\{y_1,...,y_{i-1}\\},\\mathbf{x}) = g(y_{i-1}, s_i, c_i)\n",
    "$$\n",
    "\n",
    "The current hidden state $s_i$ is calculated by an RNN $f$ with the last hidden state $s_{i-1}$, last decoder output value $y_{i-1}$, and context vector $c_i$.\n",
    "\n",
    "In the code, the RNN will be a `nn.GRU` layer, the hidden state $s_i$ will be called `hidden`, the output $y_i$ called `output`, and context $c_i$ called `context`.\n",
    "\n",
    "$$\n",
    "s_i = f(s_{i-1}, y_{i-1}, c_i)\n",
    "$$\n",
    "\n",
    "The context vector $c_i$ is a weighted sum of all encoder outputs, where each weight $a_{ij}$ is the amount of \"attention\" paid to the corresponding encoder output $h_j$.\n",
    "\n",
    "$$\n",
    "c_i = \\sum_{j=1}^{T_x} a_{ij} h_j\n",
    "$$\n",
    "\n",
    "... where each weight $a_{ij}$ is a normalized (over all steps) attention \"energy\" $e_{ij}$ ...\n",
    "\n",
    "$$\n",
    "a_{ij} = \\dfrac{exp(e_{ij})}{\\sum_{k=1}^{T} exp(e_{ik})}\n",
    "$$\n",
    "\n",
    "... where each attention energy is calculated with some function $a$ (such as another linear layer) using the last hidden state $s_{i-1}$ and that particular encoder output $h_j$:\n",
    "\n",
    "$$\n",
    "e_{ij} = a(s_{i-1}, h_j)\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Implementing the Bahdanau et al. model\n",
    "\n",
    "In summary our decoder should consist of four main parts - an embedding layer turning an input word into a vector; a layer to calculate the attention energy per encoder output; a RNN layer; and an output layer.\n",
    "\n",
    "The decoder's inputs are the last RNN hidden state $s_{i-1}$, last output $y_{i-1}$, and all encoder outputs $h_*$.\n",
    "\n",
    "* embedding layer with inputs $y_{i-1}$\n",
    "    * `embedded = embedding(last_rnn_output)`\n",
    "* attention layer $a$ with inputs $(s_{i-1}, h_j)$ and outputs $e_{ij}$, normalized to create $a_{ij}$\n",
    "    * `attn_energies[j] = attn_layer(last_hidden, encoder_outputs[j])`\n",
    "    * `attn_weights = normalize(attn_energies)`\n",
    "* context vector $c_i$ as an attention-weighted average of encoder outputs\n",
    "    * `context = sum(attn_weights * encoder_outputs)`\n",
    "* RNN layer(s) $f$ with inputs $(s_{i-1}, y_{i-1}, c_i)$ and internal hidden state, outputting $s_i$\n",
    "    * `rnn_input = concat(embedded, context)`\n",
    "    * `rnn_output, rnn_hidden = rnn(rnn_input, last_hidden)`\n",
    "* an output layer $g$ with inputs $(y_{i-1}, s_i, c_i)$, outputting $y_i$\n",
    "    * `output = out(embedded, rnn_output, context)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class BahdanauAttnDecoderRNN(nn.Module):\n",
    "    def __init__(self, hidden_size, output_size, n_layers=1, dropout_p=0.1):\n",
    "        super(AttnDecoderRNN, self).__init__()\n",
    "        \n",
    "        # Define parameters\n",
    "        self.hidden_size = hidden_size\n",
    "        self.output_size = output_size\n",
    "        self.n_layers = n_layers\n",
    "        self.dropout_p = dropout_p\n",
    "        self.max_length = max_length\n",
    "        \n",
    "        # Define layers\n",
    "        self.embedding = nn.Embedding(output_size, hidden_size)\n",
    "        self.dropout = nn.Dropout(dropout_p)\n",
    "        self.attn = GeneralAttn(hidden_size)\n",
    "        self.gru = nn.GRU(hidden_size * 2, hidden_size, n_layers, dropout=dropout_p)\n",
    "        self.out = nn.Linear(hidden_size, output_size)\n",
    "    \n",
    "    def forward(self, word_input, last_hidden, encoder_outputs):\n",
    "        # Note that we will only be running forward for a single decoder time step, but will use all encoder outputs\n",
    "        \n",
    "        # Get the embedding of the current input word (last output word)\n",
    "        word_embedded = self.embedding(word_input).view(1, 1, -1) # S=1 x B x N\n",
    "        word_embedded = self.dropout(word_embedded)\n",
    "        \n",
    "        # Calculate attention weights and apply to encoder outputs\n",
    "        attn_weights = self.attn(last_hidden[-1], encoder_outputs)\n",
    "        context = attn_weights.bmm(encoder_outputs.transpose(0, 1)) # B x 1 x N\n",
    "        \n",
    "        # Combine embedded input word and attended context, run through RNN\n",
    "        rnn_input = torch.cat((word_embedded, context), 2)\n",
    "        output, hidden = self.gru(rnn_input, last_hidden)\n",
    "        \n",
    "        # Final output layer\n",
    "        output = output.squeeze(0) # B x N\n",
    "        output = F.log_softmax(self.out(torch.cat((output, context), 1)))\n",
    "        \n",
    "        # Return final output, hidden state, and attention weights (for visualization)\n",
    "        return output, hidden, attn_weights"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interpreting the Luong et al. model(s)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Effective Approaches to Attention-based Neural Machine Translation](https://arxiv.org/abs/1508.04025) by Luong et al. describe a few more attention models that offer improvements and simplifications. They describe a few \"global attention\" models, the distinction between them being the way the attention scores are calculated.\n",
    "\n",
    "The general form of the attention calculation relies on the target (decoder) side hidden state and corresponding source (encoder) side state, normalized over all states to get values summing to 1:\n",
    "\n",
    "$$\n",
    "a_t(s) = align(h_t, \\bar h_s)  = \\dfrac{exp(score(h_t, \\bar h_s))}{\\sum_{s'} exp(score(h_t, \\bar h_{s'}))}\n",
    "$$\n",
    "\n",
    "The specific \"score\" function that compares two states is either *dot*, a simple dot product between the states; *general*, a a dot product between the decoder hidden state and a linear transform of the encoder state; or *concat*, a dot product between a new parameter $v_a$ and a linear transform of the states concatenated together.\n",
    "\n",
    "$$\n",
    "score(h_t, \\bar h_s) =\n",
    "\\begin{cases}\n",
    "h_t ^\\top \\bar h_s & dot \\\\\n",
    "h_t ^\\top \\textbf{W}_a \\bar h_s & general \\\\\n",
    "v_a ^\\top \\textbf{W}_a [ h_t ; \\bar h_s ] & concat\n",
    "\\end{cases}\n",
    "$$\n",
    "\n",
    "The modular definition of these scoring functions gives us an opportunity to build specific attention module that can switch between the different score methods. The input to this module is always the hidden state (of the decoder RNN) and set of encoder outputs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Attn(nn.Module):\n",
    "    def __init__(self, method, hidden_size, max_length=MAX_LENGTH):\n",
    "        super(Attn, self).__init__()\n",
    "        \n",
    "        self.method = method\n",
    "        self.hidden_size = hidden_size\n",
    "        \n",
    "        if self.method == 'general':\n",
    "            self.attn = nn.Linear(self.hidden_size, hidden_size)\n",
    "\n",
    "        elif self.method == 'concat':\n",
    "            self.attn = nn.Linear(self.hidden_size * 2, hidden_size)\n",
    "            self.other = nn.Parameter(torch.FloatTensor(1, hidden_size))\n",
    "\n",
    "    def forward(self, hidden, encoder_outputs):\n",
    "        seq_len = len(encoder_outputs)\n",
    "\n",
    "        # Create variable to store attention energies\n",
    "        attn_energies = Variable(torch.zeros(seq_len)) # B x 1 x S\n",
    "        if USE_CUDA: attn_energies = attn_energies.cuda()\n",
    "\n",
    "        # Calculate energies for each encoder output\n",
    "        for i in range(seq_len):\n",
    "            attn_energies[i] = self.score(hidden, encoder_outputs[i])\n",
    "\n",
    "        # Normalize energies to weights in range 0 to 1, resize to 1 x 1 x seq_len\n",
    "        return F.softmax(attn_energies).unsqueeze(0).unsqueeze(0)\n",
    "    \n",
    "    def score(self, hidden, encoder_output):\n",
    "        \n",
    "        if self.method == 'dot':\n",
    "            energy = hidden.dot(encoder_output)\n",
    "            return energy\n",
    "        \n",
    "        elif self.method == 'general':\n",
    "            energy = self.attn(encoder_output)\n",
    "            energy = hidden.dot(energy)\n",
    "            return energy\n",
    "        \n",
    "        elif self.method == 'concat':\n",
    "            energy = self.attn(torch.cat((hidden, encoder_output), 1))\n",
    "            energy = self.other.dot(energy)\n",
    "            return energy"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can build a decoder that plugs this Attn module in after the RNN to calculate attention weights, and apply those weights to the encoder outputs to get a context vector."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class AttnDecoderRNN(nn.Module):\n",
    "    def __init__(self, attn_model, hidden_size, output_size, n_layers=1, dropout_p=0.1):\n",
    "        super(AttnDecoderRNN, self).__init__()\n",
    "        \n",
    "        # Keep parameters for reference\n",
    "        self.attn_model = attn_model\n",
    "        self.hidden_size = hidden_size\n",
    "        self.output_size = output_size\n",
    "        self.n_layers = n_layers\n",
    "        self.dropout_p = dropout_p\n",
    "        \n",
    "        # Define layers\n",
    "        self.embedding = nn.Embedding(output_size, hidden_size)\n",
    "        self.gru = nn.GRU(hidden_size * 2, hidden_size, n_layers, dropout=dropout_p)\n",
    "        self.out = nn.Linear(hidden_size * 2, output_size)\n",
    "        \n",
    "        # Choose attention model\n",
    "        if attn_model != 'none':\n",
    "            self.attn = Attn(attn_model, hidden_size)\n",
    "    \n",
    "    def forward(self, word_input, last_context, last_hidden, encoder_outputs):\n",
    "        # Note: we run this one step at a time\n",
    "        \n",
    "        # Get the embedding of the current input word (last output word)\n",
    "        word_embedded = self.embedding(word_input).view(1, 1, -1) # S=1 x B x N\n",
    "        \n",
    "        # Combine embedded input word and last context, run through RNN\n",
    "        rnn_input = torch.cat((word_embedded, last_context.unsqueeze(0)), 2)\n",
    "        rnn_output, hidden = self.gru(rnn_input, last_hidden)\n",
    "\n",
    "        # Calculate attention from current RNN state and all encoder outputs; apply to encoder outputs\n",
    "        attn_weights = self.attn(rnn_output.squeeze(0), encoder_outputs)\n",
    "        context = attn_weights.bmm(encoder_outputs.transpose(0, 1)) # B x 1 x N\n",
    "        \n",
    "        # Final output layer (next word prediction) using the RNN hidden state and context vector\n",
    "        rnn_output = rnn_output.squeeze(0) # S=1 x B x N -> B x N\n",
    "        context = context.squeeze(1)       # B x S=1 x N -> B x N\n",
    "        output = F.log_softmax(self.out(torch.cat((rnn_output, context), 1)))\n",
    "        \n",
    "        # Return final output, hidden state, and attention weights (for visualization)\n",
    "        return output, context, hidden, attn_weights"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Testing the models\n",
    "\n",
    "To make sure the Encoder and Decoder model are working (and working together) we'll do a quick test with fake word inputs:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false,
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "EncoderRNN (\n",
      "  (embedding): Embedding(10, 10)\n",
      "  (gru): GRU(10, 10, num_layers=2)\n",
      ")\n",
      "AttnDecoderRNN (\n",
      "  (embedding): Embedding(10, 10)\n",
      "  (gru): GRU(20, 10, num_layers=2, dropout=0.1)\n",
      "  (out): Linear (20 -> 10)\n",
      "  (attn): Attn (\n",
      "    (attn): Linear (10 -> 10)\n",
      "  )\n",
      ")\n",
      "torch.Size([1, 10]) torch.Size([2, 1, 10]) torch.Size([1, 1, 3])\n",
      "torch.Size([1, 10]) torch.Size([2, 1, 10]) torch.Size([1, 1, 3])\n",
      "torch.Size([1, 10]) torch.Size([2, 1, 10]) torch.Size([1, 1, 3])\n"
     ]
    }
   ],
   "source": [
    "encoder_test = EncoderRNN(10, 10, 2)\n",
    "decoder_test = AttnDecoderRNN('general', 10, 10, 2)\n",
    "print(encoder_test)\n",
    "print(decoder_test)\n",
    "\n",
    "encoder_hidden = encoder_test.init_hidden()\n",
    "word_input = Variable(torch.LongTensor([1, 2, 3]))\n",
    "if USE_CUDA:\n",
    "    encoder_test.cuda()\n",
    "    word_input = word_input.cuda()\n",
    "encoder_outputs, encoder_hidden = encoder_test(word_input, encoder_hidden)\n",
    "\n",
    "word_inputs = Variable(torch.LongTensor([1, 2, 3]))\n",
    "decoder_attns = torch.zeros(1, 3, 3)\n",
    "decoder_hidden = encoder_hidden\n",
    "decoder_context = Variable(torch.zeros(1, decoder_test.hidden_size))\n",
    "\n",
    "if USE_CUDA:\n",
    "    decoder_test.cuda()\n",
    "    word_inputs = word_inputs.cuda()\n",
    "    decoder_context = decoder_context.cuda()\n",
    "\n",
    "for i in range(3):\n",
    "    decoder_output, decoder_context, decoder_hidden, decoder_attn = decoder_test(word_inputs[i], decoder_context, decoder_hidden, encoder_outputs)\n",
    "    print(decoder_output.size(), decoder_hidden.size(), decoder_attn.size())\n",
    "    decoder_attns[0, i] = decoder_attn.squeeze(0).cpu().data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Training\n",
    "\n",
    "## Defining a training iteration\n",
    "\n",
    "To train we first run the input sentence through the encoder word by word, and keep track of every output and the latest hidden state. Next the decoder is given the last hidden state of the decoder as its first hidden state, and the `<SOS>` token as its first input. From there we iterate to predict a next token from the decoder.\n",
    "\n",
    "### Teacher Forcing and Scheduled Sampling\n",
    "\n",
    "\"Teacher Forcing\", or maximum likelihood sampling, means using the real target outputs as each next input when training. The alternative is using the decoder's own guess as the next input. Using teacher forcing may cause the network to converge faster, but [when the trained network is exploited, it may exhibit instability](http://minds.jacobs-university.de/sites/default/files/uploads/papers/ESNTutorialRev.pdf).\n",
    "\n",
    "You can observe outputs of teacher-forced networks that read with coherent grammar but wander far from the correct translation - you could think of it as having learned how to listen to the teacher's instructions, without learning how to venture out on its own.\n",
    "\n",
    "The solution to the teacher-forcing \"problem\" is known as [Scheduled Sampling](https://arxiv.org/abs/1506.03099), which simply alternates between using the target values and predicted values when training. We will randomly choose to use teacher forcing with an if statement while training - sometimes we'll feed use real target as the input (ignoring the decoder's output), sometimes we'll use the decoder's output."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "teacher_forcing_ratio = 0.5\n",
    "clip = 5.0\n",
    "\n",
    "def train(input_variable, target_variable, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion, max_length=MAX_LENGTH):\n",
    "\n",
    "    # Zero gradients of both optimizers\n",
    "    encoder_optimizer.zero_grad()\n",
    "    decoder_optimizer.zero_grad()\n",
    "    loss = 0 # Added onto for each word\n",
    "\n",
    "    # Get size of input and target sentences\n",
    "    input_length = input_variable.size()[0]\n",
    "    target_length = target_variable.size()[0]\n",
    "\n",
    "    # Run words through encoder\n",
    "    encoder_hidden = encoder.init_hidden()\n",
    "    encoder_outputs, encoder_hidden = encoder(input_variable, encoder_hidden)\n",
    "    \n",
    "    # Prepare input and output variables\n",
    "    decoder_input = Variable(torch.LongTensor([[SOS_token]]))\n",
    "    decoder_context = Variable(torch.zeros(1, decoder.hidden_size))\n",
    "    decoder_hidden = encoder_hidden # Use last hidden state from encoder to start decoder\n",
    "    if USE_CUDA:\n",
    "        decoder_input = decoder_input.cuda()\n",
    "        decoder_context = decoder_context.cuda()\n",
    "\n",
    "    # Choose whether to use teacher forcing\n",
    "    use_teacher_forcing = random.random() < teacher_forcing_ratio\n",
    "    if use_teacher_forcing:\n",
    "        \n",
    "        # Teacher forcing: Use the ground-truth target as the next input\n",
    "        for di in range(target_length):\n",
    "            decoder_output, decoder_context, decoder_hidden, decoder_attention = decoder(decoder_input, decoder_context, decoder_hidden, encoder_outputs)\n",
    "            loss += criterion(decoder_output, target_variable[di])\n",
    "            decoder_input = target_variable[di] # Next target is next input\n",
    "\n",
    "    else:\n",
    "        # Without teacher forcing: use network's own prediction as the next input\n",
    "        for di in range(target_length):\n",
    "            decoder_output, decoder_context, decoder_hidden, decoder_attention = decoder(decoder_input, decoder_context, decoder_hidden, encoder_outputs)\n",
    "            loss += criterion(decoder_output, target_variable[di])\n",
    "            \n",
    "            # Get most likely word index (highest value) from output\n",
    "            topv, topi = decoder_output.data.topk(1)\n",
    "            ni = topi[0][0]\n",
    "            \n",
    "            decoder_input = Variable(torch.LongTensor([[ni]])) # Chosen word is next input\n",
    "            if USE_CUDA: decoder_input = decoder_input.cuda()\n",
    "\n",
    "            # Stop at end of sentence (not necessary when using known targets)\n",
    "            if ni == EOS_token: break\n",
    "\n",
    "    # Backpropagation\n",
    "    loss.backward()\n",
    "    torch.nn.utils.clip_grad_norm(encoder.parameters(), clip)\n",
    "    torch.nn.utils.clip_grad_norm(decoder.parameters(), clip)\n",
    "    encoder_optimizer.step()\n",
    "    decoder_optimizer.step()\n",
    "    \n",
    "    return loss.data[0] / target_length"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally helper functions to print time elapsed and estimated time remaining, given the current time and progress."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def as_minutes(s):\n",
    "    m = math.floor(s / 60)\n",
    "    s -= m * 60\n",
    "    return '%dm %ds' % (m, s)\n",
    "\n",
    "def time_since(since, percent):\n",
    "    now = time.time()\n",
    "    s = now - since\n",
    "    es = s / (percent)\n",
    "    rs = es - s\n",
    "    return '%s (- %s)' % (as_minutes(s), as_minutes(rs))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Running training\n",
    "\n",
    "With everything in place we can actually initialize a network and start training.\n",
    "\n",
    "To start, we initialize models, optimizers, and a loss function (criterion)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "attn_model = 'general'\n",
    "hidden_size = 500\n",
    "n_layers = 2\n",
    "dropout_p = 0.05\n",
    "\n",
    "# Initialize models\n",
    "encoder = EncoderRNN(input_lang.n_words, hidden_size, n_layers)\n",
    "decoder = AttnDecoderRNN(attn_model, hidden_size, output_lang.n_words, n_layers, dropout_p=dropout_p)\n",
    "\n",
    "# Move models to GPU\n",
    "if USE_CUDA:\n",
    "    encoder.cuda()\n",
    "    decoder.cuda()\n",
    "\n",
    "# Initialize optimizers and criterion\n",
    "learning_rate = 0.0001\n",
    "encoder_optimizer = optim.Adam(encoder.parameters(), lr=learning_rate)\n",
    "decoder_optimizer = optim.Adam(decoder.parameters(), lr=learning_rate)\n",
    "criterion = nn.NLLLoss()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then set up variables for plotting and tracking progress:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting job 591f9f701438c4613b4c4dc7 at 2017-05-20 03:02:21\n"
     ]
    }
   ],
   "source": [
    "# Configuring training\n",
    "n_epochs = 50000\n",
    "plot_every = 200\n",
    "print_every = 1000\n",
    "\n",
    "# Keep track of time elapsed and running averages\n",
    "start = time.time()\n",
    "plot_losses = []\n",
    "print_loss_total = 0 # Reset every print_every\n",
    "plot_loss_total = 0 # Reset every plot_every"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To actually train, we call the train function many times, printing a summary as we go.\n",
    "\n",
    "*Note:* If you run this notebook you can train, interrupt the kernel, evaluate, and continue training later. You can comment out the lines above where the encoder and decoder are initialized (so they aren't reset) or simply run the notebook starting from the following cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false,
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[log] 0m 42s (1000) 1.7562\n",
      "0m 42s (- 35m 0s) (1000 2%) 3.2168\n",
      "[log] 1m 28s (2000) 3.4178\n",
      "1m 28s (- 35m 14s) (2000 4%) 2.8085\n",
      "[log] 2m 13s (3000) 1.9268\n",
      "2m 13s (- 34m 50s) (3000 6%) 2.6295\n",
      "[log] 2m 59s (4000) 3.5481\n",
      "2m 59s (- 34m 24s) (4000 8%) 2.5226\n",
      "[log] 3m 45s (5000) 2.1306\n",
      "3m 45s (- 33m 51s) (5000 10%) 2.3431\n",
      "[log] 4m 31s (6000) 2.4112\n",
      "4m 31s (- 33m 9s) (6000 12%) 2.2012\n",
      "[log] 5m 16s (7000) 2.0306\n",
      "5m 16s (- 32m 26s) (7000 14%) 2.1778\n",
      "[log] 6m 3s (8000) 0.7172\n",
      "6m 3s (- 31m 46s) (8000 16%) 2.0516\n",
      "[log] 6m 49s (9000) 0.9867\n",
      "6m 49s (- 31m 3s) (9000 18%) 1.9482\n",
      "[log] 7m 35s (10000) 0.7058\n",
      "7m 35s (- 30m 22s) (10000 20%) 1.8463\n",
      "[log] 8m 22s (11000) 4.0532\n",
      "8m 22s (- 29m 40s) (11000 22%) 1.8389\n",
      "[log] 9m 8s (12000) 0.7909\n",
      "9m 8s (- 28m 56s) (12000 24%) 1.7710\n",
      "[log] 9m 53s (13000) 3.5531\n",
      "9m 53s (- 28m 10s) (13000 26%) 1.6996\n",
      "[log] 10m 40s (14000) 0.3723\n",
      "10m 40s (- 27m 26s) (14000 28%) 1.6392\n",
      "[log] 11m 27s (15000) 1.0735\n",
      "11m 27s (- 26m 43s) (15000 30%) 1.5887\n",
      "[log] 12m 13s (16000) 0.0578\n",
      "12m 13s (- 25m 58s) (16000 32%) 1.5159\n",
      "[log] 12m 59s (17000) 1.4627\n",
      "12m 59s (- 25m 13s) (17000 34%) 1.4669\n",
      "[log] 13m 46s (18000) 2.7408\n",
      "13m 46s (- 24m 28s) (18000 36%) 1.3980\n",
      "[log] 14m 33s (19000) 0.3085\n",
      "14m 33s (- 23m 44s) (19000 38%) 1.3614\n",
      "[log] 15m 19s (20000) 0.8777\n",
      "15m 19s (- 22m 59s) (20000 40%) 1.3163\n",
      "[log] 16m 6s (21000) 1.8394\n",
      "16m 6s (- 22m 15s) (21000 42%) 1.2799\n",
      "[log] 16m 53s (22000) 0.8656\n",
      "16m 53s (- 21m 29s) (22000 44%) 1.2038\n",
      "[log] 17m 39s (23000) 3.5788\n",
      "17m 39s (- 20m 43s) (23000 46%) 1.1853\n",
      "[log] 18m 26s (24000) 1.3385\n",
      "18m 26s (- 19m 58s) (24000 48%) 1.1643\n",
      "[log] 19m 12s (25000) 0.0158\n",
      "19m 12s (- 19m 12s) (25000 50%) 1.1351\n",
      "[log] 19m 59s (26000) 0.7937\n",
      "19m 59s (- 18m 27s) (26000 52%) 1.1285\n",
      "[log] 20m 46s (27000) 1.3123\n",
      "20m 46s (- 17m 41s) (27000 54%) 1.0553\n",
      "[log] 21m 32s (28000) 1.6989\n",
      "21m 32s (- 16m 55s) (28000 56%) 1.0265\n",
      "[log] 22m 18s (29000) 2.2208\n",
      "22m 18s (- 16m 9s) (29000 57%) 0.9440\n",
      "[log] 23m 4s (30000) 0.1320\n",
      "23m 4s (- 15m 23s) (30000 60%) 0.9769\n",
      "[log] 23m 51s (31000) 0.0043\n",
      "23m 51s (- 14m 37s) (31000 62%) 0.9395\n",
      "[log] 24m 37s (32000) 0.0119\n",
      "24m 37s (- 13m 51s) (32000 64%) 0.8899\n",
      "[log] 25m 23s (33000) 0.2071\n",
      "25m 23s (- 13m 5s) (33000 66%) 0.9135\n",
      "[log] 26m 10s (34000) 0.0169\n",
      "26m 10s (- 12m 19s) (34000 68%) 0.8698\n",
      "[log] 26m 57s (35000) 0.7662\n",
      "26m 57s (- 11m 33s) (35000 70%) 0.8209\n",
      "[log] 27m 43s (36000) 0.1208\n",
      "27m 43s (- 10m 46s) (36000 72%) 0.7931\n",
      "[log] 28m 29s (37000) 0.3535\n",
      "28m 29s (- 10m 0s) (37000 74%) 0.7899\n",
      "[log] 29m 15s (38000) 1.3398\n",
      "29m 15s (- 9m 14s) (38000 76%) 0.7603\n",
      "[log] 30m 2s (39000) 0.0115\n",
      "30m 2s (- 8m 28s) (39000 78%) 0.7454\n",
      "[log] 30m 48s (40000) 0.2135\n",
      "30m 48s (- 7m 42s) (40000 80%) 0.6740\n",
      "[log] 31m 34s (41000) 1.1087\n",
      "31m 34s (- 6m 55s) (41000 82%) 0.6738\n",
      "[log] 32m 20s (42000) 0.0262\n",
      "32m 20s (- 6m 9s) (42000 84%) 0.6659\n",
      "[log] 33m 7s (43000) 1.2855\n",
      "33m 7s (- 5m 23s) (43000 86%) 0.7443\n",
      "[log] 33m 54s (44000) 0.0022\n",
      "33m 54s (- 4m 37s) (44000 88%) 0.6427\n",
      "[log] 34m 40s (45000) 0.5267\n",
      "34m 40s (- 3m 51s) (45000 90%) 0.6092\n",
      "[log] 35m 27s (46000) 0.0068\n",
      "35m 27s (- 3m 4s) (46000 92%) 0.6172\n",
      "[log] 36m 12s (47000) 0.5520\n",
      "36m 12s (- 2m 18s) (47000 94%) 0.6145\n",
      "[log] 36m 59s (48000) 0.0185\n",
      "36m 59s (- 1m 32s) (48000 96%) 0.5903\n",
      "[log] 37m 46s (49000) 0.0026\n",
      "37m 46s (- 0m 46s) (49000 98%) 0.6131\n",
      "[log] 38m 32s (50000) 0.0138\n",
      "38m 32s (- 0m 0s) (50000 100%) 0.5403\n"
     ]
    }
   ],
   "source": [
    "# Begin!\n",
    "for epoch in range(1, n_epochs + 1):\n",
    "    \n",
    "    # Get training data for this cycle\n",
    "    training_pair = variables_from_pair(random.choice(pairs))\n",
    "    input_variable = training_pair[0]\n",
    "    target_variable = training_pair[1]\n",
    "\n",
    "    # Run the train function\n",
    "    loss = train(input_variable, target_variable, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion)\n",
    "\n",
    "    # Keep track of loss\n",
    "    print_loss_total += loss\n",
    "    plot_loss_total += loss\n",
    "\n",
    "    if epoch == 0: continue\n",
    "\n",
    "    if epoch % print_every == 0:\n",
    "        print_loss_avg = print_loss_total / print_every\n",
    "        print_loss_total = 0\n",
    "        print_summary = '%s (%d %d%%) %.4f' % (time_since(start, epoch / n_epochs), epoch, epoch / n_epochs * 100, print_loss_avg)\n",
    "        print(print_summary)\n",
    "\n",
    "    if epoch % plot_every == 0:\n",
    "        plot_loss_avg = plot_loss_total / plot_every\n",
    "        plot_losses.append(plot_loss_avg)\n",
    "        plot_loss_total = 0"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plotting training loss\n",
    "\n",
    "Plotting is done with matplotlib, using the array `plot_losses` that was created while training."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fae93740828>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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\nUmoE+n/GQ0vYEKDNQgAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fae9116d160>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import matplotlib.ticker as ticker\n",
    "import numpy as np\n",
    "%matplotlib inline\n",
    "\n",
    "def show_plot(points):\n",
    "    plt.figure()\n",
    "    fig, ax = plt.subplots()\n",
    "    loc = ticker.MultipleLocator(base=0.2) # put ticks at regular intervals\n",
    "    ax.yaxis.set_major_locator(loc)\n",
    "    plt.plot(points)\n",
    "\n",
    "show_plot(plot_losses)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Evaluating the network\n",
    "\n",
    "Evaluation is mostly the same as training, but there are no targets. Instead we always feed the decoder's predictions back to itself. Every time it predicts a word, we add it to the output string. If it predicts the EOS token we stop there. We also store the decoder's attention outputs for each step to display later."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def evaluate(sentence, max_length=MAX_LENGTH):\n",
    "    input_variable = variable_from_sentence(input_lang, sentence)\n",
    "    input_length = input_variable.size()[0]\n",
    "    \n",
    "    # Run through encoder\n",
    "    encoder_hidden = encoder.init_hidden()\n",
    "    encoder_outputs, encoder_hidden = encoder(input_variable, encoder_hidden)\n",
    "\n",
    "    # Create starting vectors for decoder\n",
    "    decoder_input = Variable(torch.LongTensor([[SOS_token]])) # SOS\n",
    "    decoder_context = Variable(torch.zeros(1, decoder.hidden_size))\n",
    "    if USE_CUDA:\n",
    "        decoder_input = decoder_input.cuda()\n",
    "        decoder_context = decoder_context.cuda()\n",
    "\n",
    "    decoder_hidden = encoder_hidden\n",
    "    \n",
    "    decoded_words = []\n",
    "    decoder_attentions = torch.zeros(max_length, max_length)\n",
    "    \n",
    "    # Run through decoder\n",
    "    for di in range(max_length):\n",
    "        decoder_output, decoder_context, decoder_hidden, decoder_attention = decoder(decoder_input, decoder_context, decoder_hidden, encoder_outputs)\n",
    "        decoder_attentions[di,:decoder_attention.size(2)] += decoder_attention.squeeze(0).squeeze(0).cpu().data\n",
    "\n",
    "        # Choose top word from output\n",
    "        topv, topi = decoder_output.data.topk(1)\n",
    "        ni = topi[0][0]\n",
    "        if ni == EOS_token:\n",
    "            decoded_words.append('<EOS>')\n",
    "            break\n",
    "        else:\n",
    "            decoded_words.append(output_lang.index2word[ni])\n",
    "            \n",
    "        # Next input is chosen word\n",
    "        decoder_input = Variable(torch.LongTensor([[ni]]))\n",
    "        if USE_CUDA: decoder_input = decoder_input.cuda()\n",
    "    \n",
    "    return decoded_words, decoder_attentions[:di+1, :len(encoder_outputs)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can evaluate random sentences from the training set and print out the input, target, and output to make some subjective quality judgements:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def evaluate_randomly():\n",
    "    pair = random.choice(pairs)\n",
    "    \n",
    "    output_words, decoder_attn = evaluate(pair[0])\n",
    "    output_sentence = ' '.join(output_words)\n",
    "    \n",
    "    print('>', pair[0])\n",
    "    print('=', pair[1])\n",
    "    print('<', output_sentence)\n",
    "    print('')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false,
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "> je suis ambitieux .\n",
      "= i m ambitious .\n",
      "< i m ambitious . <EOS>\n",
      "\n"
     ]
    }
   ],
   "source": [
    "evaluate_randomly()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Visualizing attention\n",
    "\n",
    "A useful property of the attention mechanism is its highly interpretable outputs. Because it is used to weight specific encoder outputs of the input sequence, we can imagine looking where the network is focused most at each time step.\n",
    "\n",
    "You could simply run `plt.matshow(attentions)` to see attention output displayed as a matrix, with the columns being input steps and rows being output steps:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAP4AAAECCAYAAADesWqHAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAChVJREFUeJzt3c+LXYUdhvH3bTpmTLQIbSpJRpouWkGkjeWSLiKFpmhS\nFdulgq6E2bQQaUHq0n9A3HQTVNqiVQQVirWGWCMS0GgSR2sSFRFLE4XRimgqjSa+XcydEpPYOTHn\n3HOa7/OBITPJ5c5LkmfO/TFzj5MIQC1f6XsAgMkjfKAgwgcKInygIMIHCiJ8oKDBhm97i+3XbL9h\n+zcD2HOv7Xnbr/S9ZZHtS2zvtH3A9n7bWwewadr287ZfGm+6o+9Ni2wvs/2i7cf63rLI9lu2/2Z7\nzvaeiX3eIT6Pb3uZpNclXSXpkKQXJN2Y5ECPm34k6YikPyS5vK8dJ7K9WtLqJPtsXyhpr6Sf9/z3\nZEkrkxyxPSVpl6StSZ7ra9Mi27+SNJL0tSTX9b1HWghf0ijJe5P8vEM94m+Q9EaSN5N8IulBST/r\nc1CSZyS93+eGkyV5J8m+8fsfSTooaW3Pm5LkyPjDqfFb70cX2zOSrpV0d99bhmCo4a+V9I8TPj6k\nnv9DD53tdZKukLS73yX/vUk9J2le0o4kvW+SdJek2yR91veQk0TSk7b32p6d1Ccdavg4A7YvkPSw\npFuTfNj3niTHk6yXNCNpg+1e7xrZvk7SfJK9fe74AleO/65+KukX47uUnRtq+IclXXLCxzPj38NJ\nxvejH5Z0f5JH+t5zoiQfSNopaUvPUzZKun58f/pBSZts39fvpAVJDo9/nZf0qBbu5nZuqOG/IOk7\ntr9t+zxJN0j6U8+bBmf8QNo9kg4mubPvPZJke5Xti8bvn6+FB2hf7XNTktuTzCRZp4X/S08luanP\nTZJke+X4QVnZXinpakkTedZokOEnOSbpl5K2a+EBq4eS7O9zk+0HJD0r6VLbh2zf0ueesY2SbtbC\nEWxu/HZNz5tWS9pp+2UtfAHfkWQwT58NzMWSdtl+SdLzkv6c5IlJfOJBPp0HoFuDPOID6BbhAwUR\nPlAQ4QMFET5Q0KDDn+S3MDY1xE3SMHexqZk+Ng06fEmD+0fSMDdJw9zFpmYIH0D3OvkGnvO8PNNa\nedbX86mOakrLW1jUnrY3ffd7H7dyPe/+87hWfX1ZK9f1+ssrWrmeCv9+bWhz07/1L32So17qcl9t\n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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7faebc8a2b00>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "output_words, attentions = evaluate(\"je suis trop froid .\")\n",
    "plt.matshow(attentions.numpy())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For a better viewing experience we will do the extra work of adding axes and labels:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "def show_attention(input_sentence, output_words, attentions):\n",
    "    # Set up figure with colorbar\n",
    "    fig = plt.figure()\n",
    "    ax = fig.add_subplot(111)\n",
    "    cax = ax.matshow(attentions.numpy(), cmap='bone')\n",
    "    fig.colorbar(cax)\n",
    "\n",
    "    # Set up axes\n",
    "    ax.set_xticklabels([''] + input_sentence.split(' ') + ['<EOS>'], rotation=90)\n",
    "    ax.set_yticklabels([''] + output_words)\n",
    "\n",
    "    # Show label at every tick\n",
    "    ax.xaxis.set_major_locator(ticker.MultipleLocator(1))\n",
    "    ax.yaxis.set_major_locator(ticker.MultipleLocator(1))\n",
    "\n",
    "    plt.show()\n",
    "    plt.close()\n",
    "\n",
    "def evaluate_and_show_attention(input_sentence):\n",
    "    output_words, attentions = evaluate(input_sentence)\n",
    "    print('input =', input_sentence)\n",
    "    print('output =', ' '.join(output_words))\n",
    "    show_attention(input_sentence, output_words, attentions)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input = elle a cinq ans de moins que moi .\n",
      "output = she s five years younger than me . <EOS>\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fae8dec6f98>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "evaluate_and_show_attention(\"elle a cinq ans de moins que moi .\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input = elle est trop petit .\n",
      "output = she s too short . <EOS>\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fae8d1a0c88>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "evaluate_and_show_attention(\"elle est trop petit .\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input = je ne crains pas de mourir .\n",
      "output = i m not scared to die . <EOS>\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7faebcbac6d8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "evaluate_and_show_attention(\"je ne crains pas de mourir .\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "input = c est un jeune directeur plein de talent .\n",
      "output = he s a very young young . <EOS>\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7fae8d1469b0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "evaluate_and_show_attention(\"c est un jeune directeur plein de talent .\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# Exercises\n",
    "\n",
    "* Try with a different dataset\n",
    "    * Another language pair\n",
    "    * Human &rarr; Machine (e.g. IOT commands)\n",
    "    * Chat &rarr; Response\n",
    "    * Question &rarr; Answer\n",
    "* Replace the embedding pre-trained word embeddings such as word2vec or GloVe\n",
    "* Try with more layers, more hidden units, and more sentences. Compare the training time and results.\n",
    "* If you use a translation file where pairs have two of the same phrase (`I am test \\t I am test`), you can use this as an autoencoder. Try this:\n",
    "    * Train as an autoencoder\n",
    "    * Save only the Encoder network\n",
    "    * Train a new Decoder for translation from there"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python [default]",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
